<?php
/**
 * 前端控制器定义
 * 初始化系统信息,初始化请求对象、组件工厂、应用实例对象等。加载系统配置、组件配置，并进行解析。
 *
 * @author Qiong Wu <papa0924@gmail.com> 2011-12-27
 * @copyright ©2003-2103 phpwind.com
 * @license http://www.windframework.com
 *
 * @version $$Id$$
 */
abstract class AbstractWindFrontController
{
    /**
     * 响应对象
     *
     * @var WindHttpResponse
     */
    protected $_response = null;
    /**
     * 请求对象
     *
     * @var WindHttpRequest
     */
    protected $_request = null;
    /**
     * 组件工程实例对象
     *
     * @var WindFactory
     */
    protected $_factory = null;
    /**
     * 应用配置.
     *
     * @var array
     */
    protected $_config = array(
        'isclosed' => false,
        'web-apps' => array(
            'default' => array(
                'error-dir' => 'WIND:web.view',
                'compress'  => true,
                'root-path' => '',
                'charset'   => 'utf-8',
                'modules'   => array(
                    'default' => array(
                        'controller-path'   => 'controller',
                        'controller-suffix' => 'Controller',
                        'error-handler'     => 'WindErrorHandler', ), ), ), ),
        'components' => array(), );
    /**
     * 当前app名称.
     *
     * @var string
     */
    protected $_appName = 'default';
    /**
     * 应用对象数组.
     *
     * @var WindWebApplication
     */
    protected $_app = null;
    /**
     * @var WindHandlerInterceptorChain
     */
    private $_chain = null;

    /**
     * @param string       $appName
     *                              默认app名称
     * @param array|string $config
     *                              应用配置信息,支持为空或多应用配置
     */
    public function __construct($appName, $config)
    {
        $appName && $this->_appName = $appName;
        $this->init($config);
    }

    /**
     * 预加载系统文件,返回预加载系统文件数据
     * 预加载系统文件格式如下，键值为类名=>值为类的includePath，可以是相对的（如果includePath中已经包含了该地址）
     * 也可以是绝对地址，但不能是wind的命名空间形式的地址<pre>
     * return array(
     * 'WindController' => 'web/WindController',
     * 'WindDispatcher' => 'web/WindDispatcher'
     * </pre>.
     *
     * @return array
     */
    abstract protected function _loadBaseLib();

    /**
     * 返回组建定义信息
     * 组件的配置标签说明：
     * name: 组件的名字，唯一用于在应用中获取对应组件的对象实例
     * path: 该组件的实现
     * scope: 组件对象的范围： {singleton: 单例; application: 整个应用； prototype: 当前使用}
     * initMethod: 在应用对象生成时执行的方法
     * destroy： 在应用结束的时候执行的操作
     * proxy： 组件是否用代理的方式调用
     * constructor-args：构造方法的参数
     * constructor-arg：
     * name：参数的位置,起始位置从0开始，第一个参数为0，第二个参数为1
     * 参数的值的表示方式有一下几种：
     * ref: 该属性是一个对象，ref的值对应着组件的名字
     * value: 一个字串值
     * path: path指向的类的实例将会被创建传递给该属性
     * properties: 属性的配置，表现为组件中的类属性
     * property:
     * name:属性名称
     * 属性值的表示方式有以下几种：
     * ref: 该属性是一个对象，ref的值对应着组件的名字，表现为在组件中获取方式为“_get+属性名()”称来获取
     * value: 一个字串值
     * path: path指向的类的实例将会被创建传递给该属性
     * config： 组件的配置-该值对应的配置会通过setConfig接口传递给组件；
     * resource: 指定一个外部地址，将会去包含该文件.
     *
     * @return array()
     */
    abstract protected function _components();

    /**
     * 创建并初始化应用配置.
     *
     * @param array       $config
     *                             当前应用配置信息
     * @param WindFactory $factory
     *
     * @return WindWebApplication
     */
    abstract protected function createApplication($config, $factory);

    /**
     * 初始化前端控制器
     * <ul>
     * <li>初始化应用配置<li>
     * <li>加载类加载器，拥有系统初始化加速</li>
     * <li>初始化组建工厂，组建工厂的组建分为，系统配置和自定义配置</li>
     * <li>初始化应用根目录</li>
     * </ul>.
     *
     * @param array|string $config
     *                             应用配置
     */
    protected function init($config)
    {
        $factory = WindFactory::_getInstance();
        $factory->setClassDefinitions($this->_components());
        $this->_loadBaseLib();
        $this->_initConfig($config, $factory);
        if (!empty($this->_config['components'])) {
            if (!empty($this->_config['components']['resource'])) {
                $this->_config['components'] += $factory->getInstance('configParser')->parse(
                    Wind::getRealPath($this->_config['components']['resource'], true, true));
            }
            $factory->loadClassDefinitions($this->_config['components']);
        }
        $rootPath = empty($this->_config['web-apps'][$this->_appName]['root-path']) ? dirname(
            $_SERVER['SCRIPT_FILENAME']) : Wind::getRealPath(
            $this->_config['web-apps'][$this->_appName]['root-path'], false);
        Wind::register($rootPath, $this->_appName, true);

        if ($this->_appName === 'default') {
        } elseif (isset(
            $this->_config['web-apps'][$this->_appName])) {
            $this->_config['web-apps'][$this->_appName] = WindUtility::mergeArray(
                $this->_config['web-apps']['default'], $this->_config['web-apps'][$this->_appName]);
        } else {
            throw new WindException(
                'Your requested application \''.$this->_appName.'\' was not found on this server, please check your application configuration.',
                WindException::ERROR_SYSTEM_ERROR);
        }
    }

    /**
     * 初始化配置信息.
     *
     * @param array|string $config
     * @param WindFactory  $factory
     */
    protected function _initConfig($config, $factory)
    {
        if (!$config) {
            return;
        }
        if (is_string($config)) {
            $config = $factory->getInstance('configParser')->parse($config);
        }
        $this->_config = WindUtility::mergeArray($this->_config, $config);
    }

    /**
     * 创建并执行当前应用,单应用访问入口.
     */
    public function run()
    {
        $this->_app = $this->createApplication($this->_config['web-apps'][$this->_appName],
            WindFactory::_getInstance());

        set_error_handler(array($this, '_errorHandle'), error_reporting());
        set_exception_handler(array($this, '_exceptionHandle'));
        if ($this->_config['isclosed']) {
            throw new Exception('Sorry, Site has been closed!');
        }
        if ($this->_chain !== null) {
            $this->_chain->getHandler()->handle('onCreate');
        }
        /* @var $router WindRouter */
        $router = $this->_app->getFactory()->getInstance('router');
        $router->route($this->_app->getRequest());

        if ($this->_chain !== null) {
            $this->_chain->getHandler()->handle('onStart');
        }
        $this->_app->run($router);

        if ($this->_chain !== null) {
            $this->_chain->getHandler()->handle('onResponse');
        }
        $this->_app->getResponse()->sendResponse();
        $this->_app->getFactory()->executeDestroyMethod();
        restore_error_handler();
        restore_exception_handler();
    }

    /**
     * 注册过滤器,监听Application Run.
     *
     * @param WindHandlerInterceptor $filter
     */
    public function registeFilter($filter)
    {
        if ($this->_chain === null) {
            $this->_chain = new WindHandlerInterceptorChain();
        }
        if ($filter instanceof AbstractWindBootstrap) {
            $this->_chain->addInterceptors($filter);
        }
    }

    /**
     * 错误处理句柄.
     *
     * @param int    $errno
     * @param string $errstr
     * @param string $errfile
     * @param int    $errline
     */
    public function _errorHandle($errno, $errstr, $errfile, $errline)
    {
        if (0 === error_reporting()) {
            return;
        }
        restore_error_handler();
        /* @var $error WindError */
        $error = $this->_app->getFactory()->getInstance('error',
            array(
                $this->_config['web-apps'][$this->_appName]['error-dir'],
                $this->_config['isclosed'], ));
        $error->errorHandle($errno, $errstr, $errfile, $errline);
    }

    /**
     * Exception处理.
     *
     * @param Exception $exception
     */
    public function _exceptionHandle($exception)
    {
        restore_exception_handler();
        /* @var $error WindError */
        $error = $this->_app->getFactory()->getInstance('error',
            array(
                $this->_config['web-apps'][$this->_appName]['error-dir'],
                $this->_config['isclosed'], ));
        $error->exceptionHandle($exception);
    }

    /**
     * 根据配置名取得相应的配置
     * 当<i>configName</i>为空时则返回整个配置.当配置值不存在时返回默认值.默认值默认为空.
     *
     * @param string $configName    键名
     * @param string $subConfigName 二级键名
     * @param string $default       默认值
     * @param array  $config        外部配置
     *
     * @return mixed
     */
    public function getConfig($configName = '', $subConfigName = '', $default = '')
    {
        if ($configName === '') {
            return $this->_config;
        }
        if (!isset($this->_config[$configName])) {
            return $default;
        }
        if ($subConfigName === '') {
            return $this->_config[$configName];
        }
        if (!isset($this->_config[$configName][$subConfigName])) {
            return $default;
        }

        return $this->_config[$configName][$subConfigName];
    }

    /**
     * 返回当前app应用名称.
     *
     * @return string
     */
    public function getAppName()
    {
        return $this->_appName;
    }

    /**
     * 返回当前的app应用.
     *
     * @param string $appName
     *
     * @return WindWebApplication
     */
    public function getApp()
    {
        return $this->_app;
    }
}
